#include "amfDecoder.h"
#include "umapi_libc.h"
#include "umlog.h"

#define TARGET "AMFDECODE"

static UM_U8 peek(const Decoder *dec)
{
	if (dec->pos >= dec->size) {
		return -1;
	}
	return (UM_U8)(dec->buf[dec->pos]);
}

static UM_U8 get_byte(Decoder *dec)
{
	if (dec->version == 0 && peek(dec) == AMF0_SWITCH_AMF3) {
		debug("entering AMF3 mode\n");
		dec->pos++;
		dec->version = 3;
	}

	if (dec->pos >= dec->size) {
		return -1;
	}
	return (UM_U8)(dec->buf[dec->pos++]);
}

static UM_U32 load_amf3_integer(Decoder *dec)
{
	UM_U32 value = 0;
	for (int i = 0; i < 4; ++i) {
		UM_U8 b = get_byte(dec);
		if (i == 3) {
			/* use all bits from 4th byte */
			value = (value << 8) | b;
			break;
		}
		value = (value << 7) | (b & 0x7f);
		if ((b & 0x80) == 0)
			break;
	}
	return value;
}


UM_CHAR* amf_load_string(Decoder *dec)
{
	UM_S32 str_len = 0;
	UM_U8 type = get_byte(dec);
	if (dec->version == 3) {
		if (type != AMF3_STRING) {
			return UM_NULL;
		}
		str_len = load_amf3_integer(dec) / 2;

	} else {
		if (type != AMF0_STRING) {
			return UM_NULL;
		}
		if (dec->pos + 2 > dec->size) {
			return UM_NULL;
		}
		str_len = load_be16(&dec->buf[dec->pos]);
		dec->pos += 2;
	}
	if (dec->pos + str_len > dec->size) {
		return UM_NULL;
	}
	UM_CHAR * s = umapi_malloc(str_len + 1); 
	umapi_memcpy(s, dec->buf + dec->pos, str_len);
	s[str_len]= '\0';
	dec->pos += str_len;
	return s;
}

UM_DOUBLE amf_load_number(Decoder *dec)
{
	if (get_byte(dec) != AMF0_NUMBER) {
		return 0;
	}
	if (dec->pos + 8 > dec->size) {
		return 0;
	}
	UM_U64 val = (((UM_U64)(load_be32(&dec->buf[dec->pos])) << 32) |
			(load_be32(&dec->buf[dec->pos + 4])));
//	UM_DOUBLE n = 0;
#if 0
	UM_U8 *pn = &n;
	UM_U8 *pval = &val;
	for(UM_S32 i =0; i<8; i++)
	{
		pn[7-i] = pval[i];
	}
#endif	
	//memcpy(&n, &val, 8);
	dec->pos += 8;
	return *((UM_DOUBLE*)&val);
}

UM_S32 amf_load_integer(Decoder *dec)
{
	if (dec->version == 3) {
		return load_amf3_integer(dec);
	} else {
		return amf_load_number(dec);
	}
}

UM_BOOL amf_load_boolean(Decoder *dec)
{
	if (get_byte(dec) != AMF0_BOOLEAN) {
		return UM_FALSE;
	}
	return get_byte(dec) != 0;
}

UM_CHAR* amf_load_key(Decoder *dec)
{
	if (dec->pos + 2 > dec->size) {
		return UM_NULL;
	}
	UM_S32 str_len = load_be16(&dec->buf[dec->pos]);
	dec->pos += 2;
	if (dec->pos + str_len > dec->size) {
		return UM_NULL;
	}
	UM_CHAR * s = umapi_malloc(str_len + 1); 
	umapi_memcpy(s, dec->buf + dec->pos, str_len);
	s[str_len]= '\0';
	dec->pos += str_len;
	return s;
}

amf_object_t* amf_load_object(Decoder *dec)
{

	if (get_byte(dec) != AMF0_OBJECT) {
		return UM_NULL;
	}
	amf_object_t* object;
	initAmfObject(&object);
	UM_CHAR* key = UM_NULL; 
	while (1) {
		key = amf_load_key(dec);
		if (!key)
			break;
		if(umapi_strlen(key) == 0)
		{
			umapi_free(key);
			break;
		}
		AMFValue* value = amf_load(dec);
		AMFObject *o = (AMFObject*)umapi_malloc(sizeof(AMFObject));
		o->first = key;
		o->second = value;
		umapi_list_add(&o->head, &object->head);
	}
	if (get_byte(dec) != AMF0_OBJECT_END) {
		freeAmfObject(object);
		umapi_free(object);
		return UM_NULL;
	}
	return object;
}

amf_object_t* amf_load_ecma(Decoder *dec)
{
	if (get_byte(dec) != AMF0_ECMA_ARRAY) {
		return UM_NULL;
	}
	if (dec->pos + 4 > dec->size) {
		return UM_NULL;
	}
	amf_object_t* object;
	initAmfObject(&object);
	dec->pos += 4;
	while (1) {
		UM_CHAR * key = amf_load_key(dec);
		if (!key)
			break;

		if(umapi_strlen(key) == 0)
		{
			umapi_free(key);
			break;
		}
		AMFValue* value = amf_load(dec);
		if(value->type(value) == AMF_NUMBER)
			LOGI("key:%s,value:%lf",
			key, 
			value->as_number(value));
		else if(value->type(value) == AMF_STRING)
			LOGI("key:%s,value:%s",
			key, 
			value->as_string(value));
		
		AMFObject *o = (AMFObject*)umapi_malloc(sizeof(AMFObject));
		o->first = key;
		o->second = value;
		umapi_list_add(&o->head, &object->head);
	}
	if (get_byte(dec) != AMF0_OBJECT_END) {
		freeAmfObject(object);
		umapi_free(object);
		return UM_NULL;
	}
	return object;
}

AMFValue* amf_load(Decoder *dec)
{
	UM_U8 type = peek(dec);
	if (dec->version == 3) {
		switch (type) {
		case AMF3_STRING:
			return AMFValueFromChar(amf_load_string(dec));
		case AMF3_NUMBER:
			return AMFValueFromDouble(amf_load_number(dec));
		case AMF3_INTEGER:
			return AMFValueFromInt(amf_load_integer(dec));
		case AMF3_FALSE:
			dec->pos++;
			return AMFValueFromBool(UM_FALSE);
		case AMF3_TRUE:
			dec->pos++;
			return AMFValueFromBool(UM_TRUE);
		case AMF3_OBJECT:
			return AMFValueFromObject(amf_load_object(dec));
		case AMF3_ARRAY:
			return AMFValueFromObject(amf_load_ecma(dec));
		case AMF3_NULL:
			dec->pos++;
			return AMFValueFromDefault(AMF_NULL);
		case AMF3_UNDEFINED:
			dec->pos++;
			return AMFValueFromDefault(AMF_UNDEFINED);
		default:
			printf("Unsupported AMF3 type: %02x", type);
			return UM_NULL;
		}
	} else {
		switch (type) {
		case AMF0_STRING:
			return AMFValueFromChar(amf_load_string(dec));
		case AMF0_NUMBER:
			return AMFValueFromDouble(amf_load_number(dec));
		case AMF0_BOOLEAN:
			return AMFValueFromBool(amf_load_boolean(dec));
		case AMF0_OBJECT:
			return AMFValueFromObject(amf_load_object(dec));
		case AMF0_ECMA_ARRAY:
			return AMFValueFromObject(amf_load_ecma(dec));
		case AMF0_NULL:
			dec->pos++;
			return AMFValueFromDefault(AMF_NULL);
		case AMF0_UNDEFINED:
			dec->pos++;
			return AMFValueFromDefault(AMF_UNDEFINED);
		default:
			printf("Unsupported AMF0 type: %02x", type);
			return UM_NULL;
		}
	}
}

